package org.keyroy.map.core.overlays;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;

import org.keyroy.util.tag.Tag;
import org.keyroy.util.tag.Tag.OnFillTable;
import org.keyroy.util.tag.XmlObject;

public class ObjOverlay extends XmlObject {
	public static final String DEFAULT_GROUP_NAME = "default";
	public Hashtable<String, ObjGroup> groups = new Hashtable<String, ObjOverlay.ObjGroup>();
	public Properties properties;
	
	public ObjOverlay() {
		getDefaultGroup();
	}

	public ObjGroup createGroup(String name) {
		if (groups.containsKey(name) == false) {
			ObjGroup objGroup = new ObjGroup();
			objGroup.name = name;
			groups.put(name, objGroup);
			return objGroup;
		}
		return null;
	}

	public ObjOverlay.Point addPoint(String groupName, String name, int x, int y) {
		ObjGroup group = null;
		if (groupName == null) {
			group = getDefaultGroup();
		} else {
			group = groups.get(groupName);
		}
		if (group == null) {
			return null;
		} else {
			Point point = group.points.get(name);
			if (point == null) {
				point = new Point();
				point.name = name;
				group.points.put(name, point);
				point.parent = group;
			}
			point.x = x;
			point.y = y;
			return point;
		}
	}

	public void removePoint(Point point) {
		if (point.parent instanceof ObjGroup) {
			((ObjGroup) point.parent).points.remove(point.name);
		}
	}

	public void removeLine(Line line) {
		line.parent.points.remove(line.name);
	}

	public ObjOverlay.Line addLine(String groupName, String name, int x, int y) {
		ObjGroup group = null;
		if (groupName == null) {
			group = getDefaultGroup();
		} else {
			group = groups.get(groupName);
		}
		if (group == null) {
			return null;
		} else {
			Line line = group.lines.get(name);
			if (line == null) {
				line = new Line();
				line.name = name;
				group.lines.put(name, line);
				line.parent = group;
			}
			addLinePoint(line, x, y);
			return line;
		}
	}

	public Point addLinePoint(Line line, int x, int y) {
		Point point = line.get(x, y);
		if (point == null) {
			point = new Point();
			point.x = x;
			point.y = y;
			point.parent = line;
			line.points.add(point);
		}
		return point;
	}

	public ObjOverlay.Area addArea(String groupName, String name, int x, int y) {
		ObjGroup group = null;
		if (groupName == null) {
			group = getDefaultGroup();
		} else {
			group = groups.get(groupName);
		}
		if (group == null) {
			return null;
		} else {
			Area area = group.areas.get(name);
			if (area == null) {
				area = new Area();
				area.name = name;
				area.parent = group;
				group.areas.put(name, area);
			
			}
			addAreaPoint(area, x, y);
			return area;
		}
	}

	public Point addAreaPoint(Area area, int x, int y) {
		Point point = area.get(x, y);
		if (point == null) {
			point = new Point();
			point.x = x;
			point.y = y;
			point.parent = area;
			area.points.add(point);
			area.buf = null;
		}
		return point;
	}

	private final ObjGroup getDefaultGroup() {
		ObjGroup group = groups.get(DEFAULT_GROUP_NAME);
		if (group == null) {
			group = new ObjGroup();
			group.name = DEFAULT_GROUP_NAME;
			groups.put(DEFAULT_GROUP_NAME, group);
		}
		return group;
	}

	public boolean isDirty() {
		return groups.size() > 0;
	}

	@Override
	public void initTag(Tag tag) {
		tag.addProperties(properties);

		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup objGroup = (ObjOverlay.ObjGroup) objGroups.nextElement();
			if (objGroup.isDirty()) {
				tag.addChild(objGroup);
			}
		}
	}

	@Override
	public void initObj(Tag tag) {
		properties = tag.getProperties();
		tag.getChildObjectArray(ObjGroup.class, groups, new OnFillTable<ObjGroup>() {
			@SuppressWarnings({ "unchecked", "rawtypes" })
			@Override
			public void onFillTabel(Hashtable hashtable, ObjGroup elemet) {
				hashtable.put(elemet.name, elemet);
			}
		});
	}

	public List<Point> getPoints() {
		List<Point> list = new ArrayList<Point>();
		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
			Enumeration<Point> points = group.points.elements();
			while (points.hasMoreElements()) {
				ObjOverlay.Point point = (ObjOverlay.Point) points.nextElement();
				list.add(point);
			}
		}

		return list;
	}

	public List<Point> getPoints(int x, int y) {
		List<Point> list = new ArrayList<Point>(2);
		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
			Enumeration<Point> points = group.points.elements();
			while (points.hasMoreElements()) {
				ObjOverlay.Point point = (ObjOverlay.Point) points.nextElement();
				if (point.x == x && point.y == y) {
					list.add(point);
				}
			}
		}

		return list;
	}

	public List<Line> getLines() {
		List<Line> list = new ArrayList<Line>();
		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
			Enumeration<Line> lines = group.lines.elements();
			while (lines.hasMoreElements()) {
				Line line = lines.nextElement();
				list.add(line);
			}
		}

		return list;
	}

	public List<Line> getLines(int x, int y) {
		List<Line> list = new ArrayList<Line>();
		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
			Enumeration<Line> lines = group.lines.elements();
			while (lines.hasMoreElements()) {
				Line line = lines.nextElement();
				Point point = line.get(x, y);
				if (point != null) {
					list.add(line);
				}
			}
		}
		return list;
	}

	public List<Point> getLinesPoints(int x, int y) {
		List<Point> list = new ArrayList<Point>();
		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
			Enumeration<Line> lines = group.lines.elements();
			while (lines.hasMoreElements()) {
				Line line = lines.nextElement();
				Point point = line.get(x, y);
				if (point != null) {
					list.add(point);
				}
			}
		}
		return list;
	}

	public List<Point> getAreaPoints(int x, int y) {
		List<Point> list = new ArrayList<Point>(2);
		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
			Enumeration<Area> areas = group.areas.elements();
			while (areas.hasMoreElements()) {
				Area area = areas.nextElement();
				for (int i = 0; i < area.points.size(); i++) {
					ObjOverlay.Point point = area.points.get(i);
					if (point.x == x && point.y == y) {
						list.add(point);
					}
				}
			}
		}

		return list;
	}

	public List<Area> getAreas() {
		List<Area> list = new ArrayList<Area>();
		Enumeration<ObjGroup> objGroups = groups.elements();
		while (objGroups.hasMoreElements()) {
			ObjOverlay.ObjGroup group = (ObjOverlay.ObjGroup) objGroups.nextElement();
			Enumeration<Area> areas = group.areas.elements();
			while (areas.hasMoreElements()) {
				Area area = areas.nextElement();
				list.add(area);
			}
		}

		return list;
	}

	public static class NamedElement extends XmlObject {
		public String name;
		public Properties properties;

		@Override
		public void initTag(Tag tag) {
			tag.addParameter("name", name, name != null);
			tag.addProperties(properties);

		}

		@Override
		public void initObj(Tag tag) {
			name = tag.getParameterValue("name");
			properties = tag.getProperties();
		}
	}

	public static class ObjGroup extends NamedElement {

		public boolean isHide;
		public Hashtable<String, ObjOverlay.Point> points = new Hashtable<String, ObjOverlay.Point>();
		public Hashtable<String, ObjOverlay.Line> lines = new Hashtable<String, ObjOverlay.Line>();
		public Hashtable<String, ObjOverlay.Area> areas = new Hashtable<String, ObjOverlay.Area>();

		public boolean isDirty() {
			return properties != null || points.size() > 0 || lines.size() > 0 || areas.size() > 0;
		}

		@Override
		public void initTag(Tag tag) {
			super.initTag(tag);
			tag.addChild(points.elements());
			tag.addChild(lines.elements());
			tag.addChild(areas.elements());
		}

		@Override
		public void initObj(Tag tag) {
			super.initObj(tag);

			tag.getChildObjectArray(Point.class, points, new OnFillTable<Point>() {

				@SuppressWarnings({ "rawtypes", "unchecked" })
				@Override
				public void onFillTabel(Hashtable hashtable, Point elemet) {
					hashtable.put(elemet.name, elemet);
					elemet.parent = ObjGroup.this;
				}
			});

			tag.getChildObjectArray(Line.class, lines, new OnFillTable<Line>() {

				@SuppressWarnings({ "rawtypes", "unchecked" })
				@Override
				public void onFillTabel(Hashtable hashtable, Line elemet) {
					hashtable.put(elemet.name, elemet);
					elemet.parent = ObjGroup.this;
				}
			});

			tag.getChildObjectArray(Area.class, areas, new OnFillTable<Area>() {

				@SuppressWarnings({ "rawtypes", "unchecked" })
				@Override
				public void onFillTabel(Hashtable hashtable, Area elemet) {
					hashtable.put(elemet.name, elemet);
					elemet.parent = ObjGroup.this;
				}
			});

		}

	}

	public static class Point extends NamedElement {
		public int x, y;
		public boolean isHide;
		public NamedElement parent;

		@Override
		public void initTag(Tag tag) {
			super.initTag(tag);
			tag.addParameter("x", x);
			tag.addParameter("y", y);

		}

		@Override
		public void initObj(Tag tag) {
			super.initObj(tag);
			x = tag.getParameterIntValue("x");
			y = tag.getParameterIntValue("y");
		}
	}

	public static class Line extends NamedElement {
		public List<ObjOverlay.Point> points = new ArrayList<ObjOverlay.Point>();
		public boolean isHide;
		public ObjGroup parent;

		public Point get(int x, int y) {
			for (int i = 0; i < points.size(); i++) {
				Point point = points.get(i);
				if (point.x == x && point.y == y) {
					return point;
				}
			}
			return null;
		}

		@Override
		public void initTag(Tag tag) {
			super.initTag(tag);
			tag.addChild(points);
		}

		@Override
		public void initObj(Tag tag) {
			super.initObj(tag);
			tag.getChildObjectArray(Point.class, points);
			for (int i = 0; i < points.size(); i++) {
				points.get(i).parent = Line.this;
			}
		}
	}

	public static class Area extends Line {
		public int[] buf;
	}

}
